package com.laolu.icon.utils;

import com.laolu.icon.entity.IconInfo;
import com.laolu.icon.exception.GetHWNDException;
import com.laolu.icon.exception.GetProcessIdException;
import com.laolu.icon.exception.MyException;
import com.laolu.icon.remote.RemoteMemory;
import com.laolu.icon.remote.RemoteMemory_Struct;
import com.laolu.icon.win32.User32Extra;
import com.laolu.icon.win32.struct.LVITEMA;
import com.sun.jna.platform.win32.Kernel32;
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinDef.*;
import com.sun.jna.platform.win32.WinNT.HANDLE;
import com.sun.jna.ptr.IntByReference;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;

import java.util.LinkedList;
import java.util.List;

import static com.laolu.icon.constants.Win32MessageContsants.*;
import static com.sun.jna.platform.win32.WinNT.MAX_PATH;
import static com.sun.jna.platform.win32.WinNT.PROCESS_ALL_ACCESS;
import static com.sun.jna.platform.win32.WinUser.GW_CHILD;
import static com.sun.jna.platform.win32.WinUser.GW_HWNDNEXT;

/**
 * The type My desktop.
 */
@Slf4j
@Getter
public class MyDesktop implements AutoCloseable {
	
	private final HWND hDeskWnd;
	
	private final int iconCount;
	
	private final IntByReference processId;
	
	private final HANDLE hProcess;
	
	
	/**
	 * 构造函数
	 * @throws MyException
	 * 		初始化失败
	 */
	public MyDesktop() throws MyException {
		this.hDeskWnd = getDesktopHWND();
		this.iconCount = getDesktopIconCount();
		this.processId = getDesktopProcessId();
		this.hProcess = getHProcess();
	}
	
	/**
	 * 获取桌面进程句柄
	 * @return 桌面进程句柄
	 */
	private HANDLE getHProcess() {
		HANDLE handle = Kernel32.INSTANCE.OpenProcess(PROCESS_ALL_ACCESS, false, processId.getValue());
		log.info("打开桌面进程句柄:{}", handle);
		return handle;
	}
	
	/**
	 * 获取桌面句柄
	 * @return 桌面句柄
	 * @throws GetHWNDException
	 * 		获取桌面句柄失败
	 */
	private @NotNull HWND getDesktopHWND() throws GetHWNDException {
		//获取桌面顶级窗口句柄
		HWND hwnd = User32.INSTANCE.GetDesktopWindow();
		//获取顶级窗口的一个子窗口句柄
		hwnd = User32.INSTANCE.GetWindow(hwnd, new DWORD(GW_CHILD));
		while (hwnd != null) {
			//判断当前窗口下是否有窗口类名为"SHELLDLL_DefView"
			HWND hShellView = User32.INSTANCE.FindWindowEx(hwnd, null, "SHELLDLL_DefView", null);
			
			if (hShellView != null) {
				//判断当前窗口下是否有窗口类名为"SysListView32"
				HWND hDeskWnd = User32.INSTANCE.FindWindowEx(hShellView, null, "SysListView32", null);
				if (hDeskWnd != null) {
					log.info("桌面句柄:{}", hDeskWnd);
					return hDeskWnd;
				}
			}
			//获取当前窗口的同级别下一个窗口句柄
			hwnd = User32.INSTANCE.GetWindow(hwnd, new DWORD(GW_HWNDNEXT));
		}
		throw new GetHWNDException("获取桌面句柄失败");
	}
	
	/**
	 * 获取桌面图标数量
	 * @return 桌面图标数量
	 */
	private int getDesktopIconCount() {
		
		int count = User32.INSTANCE.SendMessage
				(hDeskWnd, LVM_GETITEMCOUNT, new WPARAM(0), new LPARAM(0)).intValue();
		log.info("桌面图标数量:{}", count);
		return count;
	}
	
	/**
	 * 获取桌面进程ID
	 * @return 桌面进程ID
	 * @throws GetProcessIdException
	 * 		获取桌面进程ID失败
	 */
	private @NotNull IntByReference getDesktopProcessId() throws GetProcessIdException {
		IntByReference processId = new IntByReference();
		int result = User32.INSTANCE.GetWindowThreadProcessId(hDeskWnd, processId);
		if (result != 0) {
			log.info("桌面进程ID:{}", processId.getValue());
			return processId;
		}
		throw new GetProcessIdException("获取桌面进程ID失败");
	}
	
	/**
	 * Is ok boolean.
	 * @return the boolean
	 */
	public boolean isOk() {
		return hProcess != null;
	}
	
	
	/**
	 * 获取桌面图标信息
	 * @return 桌面图标信息
	 * @throws MyException
	 * 		获取桌面图标信息失败
	 */
	public List<IconInfo> getDesktopIconRect() throws MyException {
		if (!isOk()) {
			throw new MyException("初始化失败！");
		}
		List<IconInfo> list = new LinkedList<>();
		RemoteMemory textRemoteMemory = new RemoteMemory(MAX_PATH, hProcess);
		RECT rect = new RECT();
		LVITEMA item = new LVITEMA();
		item.iSubItem = 0;
		item.pszText = textRemoteMemory.getRemoteAddress();
		item.cchTextMax = MAX_PATH;
		item.autoWrite();
		
		RemoteMemory_Struct<RECT> rectRemoteMemory = new RemoteMemory_Struct<>(rect, hProcess);
		RemoteMemory_Struct<LVITEMA> itemRemoteMemory = new RemoteMemory_Struct<>(item, hProcess);
		try (textRemoteMemory; rectRemoteMemory; itemRemoteMemory) {
			if (textRemoteMemory.isOk() && itemRemoteMemory.isOk() && rectRemoteMemory.isOk()) {
				//获取桌面图标个数
				int desktopIconCount = getDesktopIconCount();
				//远程在桌面窗口进程写入消息体
				if (itemRemoteMemory.write()) {
					
					
					//循环获取图标名字与位置
//					log.info("----------start get desktop icon rect info----------");
					for (int i = 0; i < desktopIconCount; i++) {
//						log.info("-----info item {}-----", i);
						try {
							String iconTitle = getIconTitle(hDeskWnd, i, itemRemoteMemory, textRemoteMemory);
							RECT iconRect = getIconRect(hDeskWnd, i, rectRemoteMemory);
							
							IconInfo iconInfo = new IconInfo();
							iconInfo.setTitle(iconTitle);
							iconInfo.setRect(MyRectUtils.copyRECT(iconRect));
							list.add(iconInfo);
						} catch (MyException e) {
							log.error(e.getMessage());
						}
					}
//					log.info("----------end get desktop icon rect info----------");
				}
			}
		} catch (Exception e) {
			log.error(e.getMessage());
		}
		
		return list;
	}
	
	/**
	 * 获取图标名称
	 * @param hDeskWnd
	 * 		桌面窗口句柄
	 * @param index
	 * 		要获取名称的图标索引
	 * @param itemRemoteMemory
	 * 		远程申请的item结构体
	 * @param textRemoteMemory
	 * 		远程申请的text结构体
	 * @return 图标名称
	 * @throws MyException
	 * 		获取图标名称失败
	 */
	private String getIconTitle(@NotNull HWND hDeskWnd, int index,
	                            @NotNull RemoteMemory_Struct<LVITEMA> itemRemoteMemory,
	                            @NotNull RemoteMemory textRemoteMemory) throws MyException {
		//发送消息获取图标文本
		User32Extra.INSTANCE.SendMessage
				(hDeskWnd, LVM_GETITEMTEXT, new WPARAM(index), itemRemoteMemory.getRemoteAddress());
		//远程读取pszText
		if (textRemoteMemory.read()) {
			return textRemoteMemory.getDATA().getString(0);
		} else {
			throw new MyException("read text error,error code:" + Kernel32.INSTANCE.GetLastError());
		}
	}
	
	/**
	 * 获取图标位置
	 * @param hDeskWnd
	 * 		桌面窗口句柄
	 * @param index
	 * 		要获取名称的图标索引
	 * @param rectRemoteMemory
	 * 		远程申请的rect结构体
	 * @return 图标位置
	 * @throws MyException
	 * 		获取图标位置失败
	 */
	private @NotNull RECT getIconRect(@NotNull HWND hDeskWnd, int index,
	                                  @NotNull RemoteMemory_Struct<RECT> rectRemoteMemory) throws MyException {
		RECT rect = rectRemoteMemory.getDATA();
		//远程初始化rect信息
		rect.left = 0;
		rect.top = 0;
		rect.right = 0;
		rect.bottom = 0;
		if (rectRemoteMemory.write()) {
			//发送消息获取图标位置
			LRESULT result = User32Extra.INSTANCE.SendMessage
					(hDeskWnd, LVM_GETITEMRECT, new WPARAM(index), rectRemoteMemory.getRemoteAddress());
			if (result.intValue() > 0 && rectRemoteMemory.read()) {
				return rect;
			}
		}
		throw new MyException("read rect error,error code:" + Kernel32.INSTANCE.GetLastError());
	}
	
	@Override
	public void close() {
		if (hProcess != null) {
			//关闭桌面窗口进程句柄
			if (Kernel32.INSTANCE.CloseHandle(hProcess)) {
				log.info("关闭桌面进程句柄:{}", hProcess);
			} else {
				log.error("关闭桌面进程句柄失败 error code:{}", Kernel32.INSTANCE.GetLastError());
			}
		}
	}
}
